#pragma once 
#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<unordered_map>
#include<mutex>
#include"tool.hpp"
#include"Log.hpp"
#include"Mutex.hpp"

namespace ns_index
{
    using namespace tool_a;
    using namespace Log_Module;
    using namespace Lock_Module;
    //ENABLE_CONSOLE_LOG();
    struct DocInfo{
        std::string title;
        std::string content;
        std::string url;
        uint64_t doc_id;
    };

    struct InvertedInfo
    {
        uint64_t doc_id;
        std::string word;
        int weight;
    };
    //倒排拉链    
    typedef std::vector<InvertedInfo> InveredList;
    class Index{
        private:
            //正排索引
            std::vector<DocInfo>_forward_index;
            //倒排索引
            std::unordered_map<std::string,InveredList> _Invered_index;
        private:
            Index(){}
            Index(const Index&)=delete;
            Index& operator=(const Index&)=delete;
            static Index*instance;
            static Mutex mtx;

        public:
            ~Index(){}

            static Index*GetInstance(){
                if(instance==nullptr)
                {
                    //利用RALL的思想
                    LockGuard guard(mtx);
                    instance=new Index();
                }
                return instance;
            }

            //获取文档内容
            DocInfo*GetForwardIndex(uint64_t doc_id){               
                if(doc_id>_forward_index.size())
                {
                    std::cerr<<"查询文档不存在"<<std::endl;
                }
                return &_forward_index[doc_id];
            }
            //获取倒排拉链
            InveredList*GetInvertedList(const std::string&word)
            {
                auto iter=_Invered_index.find(word);
                if(iter==_Invered_index.end()){std::cerr<<"获取倒排拉链失败，关键词为:"<<word<<std::endl;return nullptr;}
                return &(iter->second);
            }
            bool BuildIndex(const std::string &input)
            {
                std::ifstream in(input,std::ios::in|std::ios::binary);
                if(!in.is_open()){std::cerr<<"建立索引时文件未打开,文件路径为:"<<input<<std::endl;}
                std::string line;
                int count=0;
                while(std::getline(in,line))
                {
                    DocInfo*doc=BulidForwardIndex(line);
                    if(doc==nullptr)
                    {
                        std::cerr<<"bulid"<<line<<"error"<<std::endl;
                        continue;
                    }
                    BulidInveredIndex(*doc);
                    if(count%100==0)
                    {
                        LOG(Log_Level::INFO)<<"当前的已经建立的索引文档: "<< count;
                    }
                    count++;
                }
                return true;
            }
            private:
            DocInfo* BulidForwardIndex(const std::string&line)
            {
                std::vector<std::string> result;
                const std::string sep="\3";
                tool_a::String_tool::Split(line,&result,sep);
                if(result.size()!=3)
                {
                    std::cerr<<"建立前序索引失败"<<std::endl;
                }
                DocInfo temp;
                temp.title=result[0];
                temp.content=result[1];
                temp.url=result[2];
                temp.doc_id=_forward_index.size();
                _forward_index.push_back(move(temp));
                return &_forward_index.back();
            }
            bool BulidInveredIndex(const DocInfo&doc)
            {
                //划分标题还是内容，对相关性有影响
                struct word_cnt{
                    int title_cnt;
                    int content_cnt;
                    word_cnt():title_cnt(0),content_cnt(0){}
                };
                //存词频的映射
                std::unordered_map<std::string,word_cnt> word_map;
                //对标题进行分词
                std::vector<std::string>title_words;
                tool_a::Jieba_tool::Separate_String(doc.title,&title_words);
                //对标题的词统计词频
                for(std::string s : title_words)
                {
                    boost::to_lower(s);
                    word_map[s].title_cnt++;
                }
                //对内容进行分词
                std::vector<std::string>content_words;
                tool_a::Jieba_tool::Separate_String(doc.content,&content_words);
                //对内容进行词频的统计
                for(std::string s:content_words)
                {
                    boost::to_lower(s);
                    word_map[s].content_cnt++;
                }
#define X 10
#define Y 1
                for(auto&word_pair:word_map)
                {
                    InvertedInfo item;
                    item.doc_id=doc.doc_id;
                    item.word=word_pair.first;
                    item.weight=X*word_pair.second.title_cnt+Y*word_pair.second.content_cnt;
                    InveredList& list= _Invered_index[word_pair.first];
                    list.push_back(std::move(item));
                }
                return true;
            }
    };
    Index* Index::instance=nullptr;
    Mutex Index::mtx;
}